/**
 * @Description 初始化参数
 * @Author 莫发达
 * @create 2024-09-24 17:02 周二
 */
import { cache, def, eachTree, isObject, parseJsonNoError } from 'sf-utils2'
import sysConfigApi from '@/api/modules/cf-sys-config'
import type { IParameter, ParamKeys } from '@root/types/system/parameter'
import type { ITreeItem, ITreeItemBase } from '@root/types/tree.ts'
import { ElMessage } from 'element-plus'
import { $enums } from '@/utils/dict'
import { paramKeys } from './param-keys.ts'

type ParamKeysV2 = ParamKeys | `$.${string}`

interface IRegionItem extends ITreeItem<IRegionItem>, ITreeItemBase {
  value: string
  label: string
  children: Partial<IRegionItem>[]
}

/**
 * 原始参数缓存
 */
let PARAM = cache.getItem('PARAM') || {}

/**
 * 原始参数对象
 */
const ORIGIN_PARAM: Record<string, IParameter> = {}

/**
 * 启用的参数对象
 */
const ENABLE_PARAM: Record<string, IParameter> = {}

/**
 * 初始化系统参数
 * @returns 返回系统参数数组
 */
async function initParam() {
  // 检查全局缓存中是否存在参数
  const cachedParam = window.$zsamcVars?.PARAM

  if (cachedParam) {
    PARAM = cachedParam
    cache.setItem('PARAM', PARAM)
    return PARAM
  }

  try {
    let paramFromServer = cache.getItem('PARAM')

    // 从服务器请求系统参数并初始化
    if (!paramFromServer?.length) {
      paramFromServer = await fetchParameterFromServer()
    }

    cache.setItem('PARAM', paramFromServer)
    PARAM = paramFromServer

    Object.assign(ORIGIN_PARAM, processOriginalParameter(paramFromServer))
    Object.assign(ENABLE_PARAM, processEnabledParameter(paramFromServer))

    // 添加地址区域参数
    const addressRegionKey = '$.address.region'
    const addressRegionOriginItem = ORIGIN_PARAM['address.region'] || {}
    const addressRegionItem = {
      ...addressRegionOriginItem,
      configKey: addressRegionKey,
      configValue: handleRegion(parseJsonNoError(addressRegionOriginItem?.configValue as string) || [])
    }
    paramFromServer.push(addressRegionItem)
    ENABLE_PARAM[addressRegionKey] = ORIGIN_PARAM[addressRegionKey] = addressRegionItem
    def(ORIGIN_PARAM, addressRegionKey)
    def(ENABLE_PARAM, addressRegionKey)

    return paramFromServer
  } catch (error) {
    console.error('初始化系统参数时发生错误:', error)
  }

  /**
   * 处理地区
   */
  function handleRegion(data: IRegionItem[]) {
    const fieldKey = 'value'
    const noAddProvinName = [
      '北京',
      '上海',
      '重庆',
      '天津',
      '香港',
      '澳门',
      '台湾',
      '新疆',
      '广西',
      '西藏',
      '宁夏',
      '内蒙古'
    ] // 不需要添加省的名称
    return eachTree({
      tree: data || [],
      props: { children: 'children' } as any,
      retainField: ['__pathIds__', '__pathIdsObj__'],
      isDeepClone: true,
      callbackList(list, parentObj) {
        if (list.length) {
          const tempList = list.filter((item) => {
            if (item?.[fieldKey] && parentObj?.[fieldKey]) {
              const itemSplits = (item[fieldKey] || '').split('-').filter(Boolean)
              const parentSplits = (parentObj[fieldKey] || '').split('-').filter(Boolean)
              return getProvinceName(itemSplits.at(-1)) != getProvinceName(parentSplits.at(-1))
            }
            return true
          })
          list.length = 0
          list.push(...tempList)
        }
      },
      callbackItem(item) {
        if (item.__level__ == 1) {
          if (!noAddProvinName.includes(item.label)) {
            item.label = `${item.label}省`
          }
          if (!noAddProvinName.includes(item.value)) {
            item.value = `${item.value}省`
          }
        }
      }
    })

    function getProvinceName(name: string) {
      return (name || '').replace(/省/, '')
    }
  }
}

/**
 * 从服务器请求系统参数数据
 * @returns 返回参数数组
 * @throws 当服务器响应非200时抛出错误
 */
export async function fetchParameterFromServer(): Promise<IParameter[]> {
  const { code, message, data } = await sysConfigApi.getCfSysConfigByKeys({ keys: paramKeys })

  if (code !== 200) {
    ElMessage.error(message)
    throw new Error(message)
  }

  return data
}

/**
 * 处理原始系统参数
 * @param parameter 参数数组
 * @returns 返回处理后的原始参数对象
 */
export function processOriginalParameter(parameter: IParameter[]): Record<string, IParameter> {
  return parameter.reduce<Record<string, IParameter>>((acc, item) => {
    acc[item.configKey] = item
    return acc
  }, {})
}

/**
 * 处理启用的系统参数
 * @param parameter 参数数组
 * @returns 返回处理后的启用参数对象
 */
export function processEnabledParameter(parameter: IParameter[]): Record<string, IParameter> {
  return parameter
    .filter((item) => item.enableFlag === $enums.enableFlag.ENABLE)
    .reduce<Record<string, IParameter>>((acc, item) => {
      acc[item.configKey] = item
      return acc
    }, {})
}

/**
 * 获取指定类型的参数
 * @param key 参数键
 * @param type 参数类型
 * @param defaultValue 默认值
 * @returns 返回对应类型的参数值或默认值
 */
export function param<T extends string | boolean | Record<string, unknown> | Array<Record<string, unknown>>>(
  key: ParamKeysV2,
  type: 'STRING' | 'BOOLEAN' | 'JSON' | 'JSONARRAY',
  defaultValue?: T
): T | undefined {
  const param = ENABLE_PARAM[key]
  const configValue = param?.configValue

  // 默认值处理
  const localDefaultValue = getDefaultValue<T>(type, defaultValue)

  // 根据类型进行处理
  switch (type) {
    case 'STRING':
      return (configValue || localDefaultValue) as T // 字符串类型返回

    case 'BOOLEAN':
      return (configValue === $enums.enableFlag.ENABLE ?? localDefaultValue) as T // 布尔值类型返回

    case 'JSON':
    case 'JSONARRAY':
      try {
        // feat: 新增如果缓存中已经解析成对象了存在，那么直接取PARAM上取，不需要再重新JSON.parse了
        const tempValue = (configValue ?? localDefaultValue) as T
        if (isObject(tempValue)) return tempValue
        // 解析 JSON 数据，如果失败则返回默认值
        const value = (JSON.parse(configValue as string) ?? localDefaultValue) as T
        param.configValue = value
        return value
      } catch (e) {
        console.error(`解析 ${key} - ${type === 'JSON' ? 'JSON' : 'JSON数组'} 时发生错误`)
        return localDefaultValue as T
      }

    default:
      throw new Error(`未知的类型: ${type}`)
  }
}

/**
 * 获取默认值
 * @param type
 * @param defaultValue
 */
function getDefaultValue<T extends string | boolean | Record<string, unknown> | Array<Record<string, unknown>>>(
  type: 'STRING' | 'JSON' | 'BOOLEAN' | 'JSONARRAY',
  defaultValue: T
): T {
  switch (type) {
    case 'STRING':
      return (defaultValue || '') as T
    case 'BOOLEAN':
      return (defaultValue ?? false) as T
    case 'JSON':
      return (defaultValue || {}) as T
    case 'JSONARRAY':
      return (defaultValue || []) as T
    default:
      return defaultValue
  }
}

/**
 * 初始化系统参数
 */
export const $initParam = initParam

/**
 * 获取系统参数
 */
export const $param = param

// 参数获取函数，针对不同类型的参数进行处理

/**
 * 获取字符串类型的参数
 * @param key 参数键
 * @param defaultValue 默认值
 *
 * @example
 * const param = $paramString('key', 'default')
 */
export const $paramString = (key: ParamKeysV2, defaultValue?: string): string =>
  param<string>(key, 'STRING', defaultValue)

/**
 * 获取布尔值类型的参数
 * @param key 参数键
 * @param defaultValue 默认值
 *
 * @example
 * const param = $paramBoolean('key', true)
 */
export const $paramBoolean = (key: ParamKeysV2, defaultValue?: boolean): boolean =>
  param<boolean>(key, 'BOOLEAN', defaultValue)

/**
 * 获取 JSON 类型的参数
 * @param key 参数键
 * @param defaultValue 默认值
 *
 * @example
 * const param = $paramJSON('key', {a: 1})
 */
export const $paramJSON = (key: ParamKeysV2, defaultValue?: Record<string, unknown>): Record<string, unknown> =>
  param<Record<string, unknown>>(key, 'JSON', defaultValue)

/**
 * 获取 JSON 数组类型的参数
 * @param key 参数键
 * @param defaultValue 默认值
 *
 * @example
 * const param = $paramJSONArray('key', [{a: 1}])
 */
export const $paramJSONArray = (
  key: ParamKeysV2,
  defaultValue?: Array<Record<string, unknown>>
): Array<Record<string, unknown>> => param<Array<Record<string, unknown>>>(key, 'JSONARRAY', defaultValue)
